package com.github.baichuan.binary;


import lombok.NonNull;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

/**
 * 位操作工具
 */
public class Bits {
    public static final int BYTE_MASK = 0xFF;

    private byte[] array;

    private Bits(@NonNull byte[] array){
        this.array = array;
    }

    public static Bits of(byte[] value){
        return new Bits(value);
    }

    public static Bits of(String hex) {
        try{
            byte[] array = Hex.decodeHex(hex);
            return new Bits(array);
        }
        catch (DecoderException ex){
            throw new RuntimeException(ex);
        }
    }

    public byte[] bytes(){
        return this.array;
    }

    /**
     * 按位对数据内容进行切片
     * @param start 开始位(包含)
     * @param end 结束位(不包含)
     * @return
     */
    public Bits slice(int start, int end){
        if(start >= end){
            throw new IllegalArgumentException("start must less than end");
        }

        if(end > array.length * 8){
            throw new IllegalArgumentException("end is over size to value");
        }

        int byteStart = start / 8;
        int byteLength = (int)Math.ceil((end - start) / 8.0);

        byte[] dst = new byte[byteLength];

        if(byteIndex(start) == byteIndex(end)){
            //未跨字节
            int mask = mask(start % 8, end - start);
            int shift = (8 - (end % 8)) % 8;
            dst[0] = (byte)((array[byteStart] & mask) >>> shift);
        }
        else{
            //跨字节
            int shift = (8 - (start % 8)) % 8;
            int leftMask = mask(0, 8 - shift);
            int rightMask = mask(8 - shift, shift);

            for(int i = 0; i < dst.length; i++){
                int index = byteStart + i;

                int left = ((array[index] & leftMask) << shift) & BYTE_MASK;
                int right = 0;
                if(index + 1 < byteLength){
                    right = (array[index + 1] & rightMask) >>> (8 - shift);
                }
                int b = left | right;
                dst[i] = (byte)b;
            }
        }

        return new Bits(dst);
    }

    /**
     * 获取位索引所在的字节索引
     * @param bitIndex
     * @return
     */
    public static int byteIndex(int bitIndex){
        return (int)Math.ceil(bitIndex / 8.0);
    }

    /**
     * 生成位操作掩码
     * @return
     */
    public static int mask(int offset, int length){
        if (offset + length > 8)
            throw new IllegalStateException("offset + length 长度大于8");

        int bits = BYTE_MASK;
        bits >>= (8 - offset - length);
        bits &= BYTE_MASK;
        bits <<= (8 - length);
        bits &= BYTE_MASK;
        bits >>= offset;
        bits &= BYTE_MASK;
        return bits;
    }
}
